package com.maimao.user.service;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.maimao.core.constant.Constants;
import com.maimao.core.constant.RedisKeys;
import com.maimao.core.enums.IdCardType;
import com.maimao.core.exception.MaiMaoDaoException;
import com.maimao.core.model.resp.PageResult;
import com.maimao.core.model.resp.Result;
import com.maimao.core.utils.DateUtils;
import com.maimao.core.utils.HttpClientUtil;
import com.maimao.core.utils.IPUtil;
import com.maimao.core.utils.SecurityUtil;
import com.maimao.email.EmailSender;
import com.maimao.model.common.baidu.BaiDuIpRes;
import com.maimao.model.email.enums.EmailTypeEnum;
import com.maimao.model.user.Shop;
import com.maimao.model.user.User;
import com.maimao.model.user.enums.RoleEnum;
import com.maimao.model.user.input.RegisterInput;
import com.maimao.model.user.input.UpdateIntegralInput;
import com.maimao.model.user.sys.Resources;
import com.maimao.model.user.sys.Role;
import com.maimao.model.user.sys.vo.Permissions;
import com.maimao.model.user.vo.VerifyMethod;
import com.maimao.user.mapper.ShopMapper;
import com.maimao.user.mapper.UserMapper;
import com.maimao.user.mapper.sys.ResourcesMapper;
import com.maimao.user.mapper.sys.SysUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author MaoLin Wang
 * @date 2020/12/13 10:02 下午
 */
@Service
@Slf4j
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private ResourcesMapper resourcesMapper;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private ShopMapper shopMapper;

    @Autowired
    private EmailSender emailSender;

    @Resource(name = "userEmailSendPool")
    private ThreadPoolTaskExecutor userEmailSendPool;


    @Autowired
    private RedisTemplate redisTemplate;


    public UserService(ThreadPoolTaskExecutor userEmailSendPool) {
        this.userEmailSendPool = userEmailSendPool;
    }

    public Permissions getPermissionsByUsername(String username) {
        User user = userMapper.selectByUsernameValid(username);
        if (user == null) {
            return null;
        }
        Permissions permissions = getPermissions(user);
        return permissions;
    }

    public Permissions getPermissionsByMobile(String username) {
        User user = userMapper.selectByMobileValid(username);
        if (user == null) {
            return null;
        }
        Permissions permissions = getPermissions(user);
        return permissions;
    }

    public Permissions getPermissionsByEmail(String email) {
        User user = userMapper.selectByEmailValid(email);
        if (user == null) {
            return null;
        }
        Permissions permissions = getPermissions(user);
        return permissions;
    }

    private Permissions getPermissions(User user) {
        Permissions permissions = new Permissions();
        permissions.setId(user.getId());
        permissions.setAvatar(Constants.EMPTY);
        permissions.setUsername(user.getUsername());
        permissions.setPassword(user.getPassword());
        permissions.setMobile(user.getMobile());
        if (user.getShopStatus() != null && user.getShopStatus().equals(1)) {
            Shop shop = shopMapper.findShopByOwnerAndSocial(user.getId(), 0);
            permissions.setShopId(shop == null ? null : shop.getId());
        }

        List<Role> roles = user.getRoleList();
        if (roles != null && roles.size() > 0) {
            List<String> roleNames = roles.stream().map(Role::getRoleCode).collect(Collectors.toList());
            List<Integer> roleIds = roles.stream().map(Role::getId).collect(Collectors.toList());

            permissions.setRoles(roleNames);
            List<Resources> resourcesList = resourcesMapper.selectByRoleIdBatch(roleIds);
            if (resourcesList != null && resourcesList.size() > 0) {
                List<String> resourcesNames = resourcesList.stream().map(Resources::getCode).collect(Collectors.toList());
                permissions.setResources(resourcesNames);
            }
        }
        return permissions;
    }


    public Map<String, String> getIdCardUrlByUid(Long userId) {
        return userMapper.getIdCardUrlByUid(userId);

    }

    public User getIdCardInfoByUid(Long userId) {
        return userMapper.getIdCardInfoByUid(userId);

    }

    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public boolean updateIdCard(String userId, Map<String, String> updateParams) {
        String targetUrl = null;
        targetUrl = updateParams.get(IdCardType.FRONT.getName());
        if (targetUrl != null) {
            userMapper.updateIdUrlFront(userId, targetUrl);
            return true;
        }
        targetUrl = updateParams.get(IdCardType.BACK.getName());
        if (targetUrl != null) {
            userMapper.updateIdUrlBack(userId, targetUrl);
            return true;
        }
        targetUrl = updateParams.get(IdCardType.Hand.getName());
        if (targetUrl != null) {
            userMapper.updateIdUrlHand(userId, targetUrl);
            return true;
        }
        return false;
    }

    /**
     * 开通店铺 （修改为申请状态）
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public boolean updateShopStatus(Long userId) {
        User user = userMapper.getIdCardInfoByUid(userId);
        if (!checkIdCard(user)) {
            return false;
        }
        //0 未开店 1 正常开店 2 申请中 3 冻结中 4 关闭 5 驳回
        userMapper.updateShopStatus(userId, 2);
        return true;
    }

    private boolean checkIdCard(User user) {
        if (StringUtils.isAnyEmpty(
                user.getIdUrlBack(),
                user.getIdUrlFront(),
                user.getIdUrlHand())) {
            return false;
        }
        return true;
    }

    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public Result<Void> register(RegisterInput registerInput) {
        User mobileUser = userMapper.selectByMobile(registerInput.getMobile());
        if (mobileUser != null) {
            return Result.notValid("手机号已经被注册");
        }
        if (userMapper.selectByUsername(registerInput.getUsername()) != null) {
            return Result.notValid("会员名已经被注册");
        }
        User user = User.builder().username(registerInput.getUsername())
                .password(bCryptPasswordEncoder.encode(registerInput.getPassword()))
                .mobile(registerInput.getMobile())
                .status(1)
                .shopStatus(0)
                .createTime(DateUtils.now())
                .freezeIntegral(0)
                .integral(0).build();
        userMapper.insert(user);
        sysUserMapper.addUserRoleRecord(user.getId(), Lists.newArrayList(RoleEnum.ROLE_WEB_COMMON.getId()));
        return Result.ok("注册成功");
    }

    public Integer queryIntegral(Long userId) {
        User user = userMapper.selectIntegral(userId);
        Integer integral = 0;
        if (user.getFreezeIntegral() != null && user.getFreezeIntegral() > 0) {
            //去掉冻结积分
            integral = user.getIntegral() - user.getFreezeIntegral();
        } else {
            integral = user.getIntegral();
        }
        return integral;
    }

    /**
     * 修改用户积分
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void updateIntegral(UpdateIntegralInput integralInput) {
        if (integralInput.getCancel()) {
            //取消订单 或支付
            if (integralInput.getUseIntegral()) {
                //使用了积分 积分 += 积分减免*100(设为了this.integral) 冻结积分 -=赠送的积分
                userMapper.addIntegralAndReduceFreezeIntegral(integralInput.getIntegral(), integralInput.getUserId(), integralInput.getFreezeIntegral());
            } else {
                //未使用积分 冻结积分 -=赠送的积分
                userMapper.reduceFreezeIntegral(integralInput.getFreezeIntegral(), integralInput.getUserId());
            }
        } else {
            //下单
            if (integralInput.getUseIntegral()) {
                //使用了积分 积分 -= 积分减免*100(设为了this.integral) 冻结积分 += 赠送的积分
                userMapper.reduceIntegralAndAddFreezeIntegral(integralInput.getIntegral(), integralInput.getUserId(), integralInput.getFreezeIntegral());
            } else {
                //未使用积分 冻结积分 += 赠送的积分
                userMapper.addFreezeIntegral(integralInput.getFreezeIntegral(), integralInput.getUserId());
            }
            /*
                取消订单使用积分，积分直接=订单总减免的问题：
                   第一次下单 积分 300  使用 ==> 扣0 赠送500 未付款冻结
                   第二次下单 未使用积分 赠送 600冻结  冻结 ==> 1100
                   第二次付款 积分 => 600
                   第一次未付款自动取消  积分 ==> 300 出错了 应该是 600+300

                  下单使用积分 积分直接清零的问题：
                  可能第一次下单未付款  积分 => 0 赠送400积分（冻结）
                  第二次下单并付款  赠送积分600 积分 => 600
                  第三次进入下单页 可使用600  ，这时付款第一个订单  积分 => 1000 然后第三次下单  积分直接清零 出错x
                  应该 积分-=积分减免

             */
        }


    }

    public String queryUserName(Long id) {
        return userMapper.queryUsername(id);
    }

    public List<User> queryShopCheck() {
        return userMapper.queryShopCheck();
    }

    public void sendBackEmail(String email, Integer type, HttpServletRequest request, String userId) {
        userEmailSendPool.submit(() -> {
            log.info("准备发送邮件..........");
            String ipStr = IPUtil.getIpStr(request);
            boolean ipAddrRequestErr = false;
            Result<String> requestAddressRes = null;
            try {
                requestAddressRes = HttpClientUtil.doGet(StrUtil.format(baiduIpAddr, ipStr));
            } catch (IOException e) {
                e.printStackTrace();
                log.error("获取IP地理位置出错,ip:{}", ipStr);
                ipAddrRequestErr = true;
            }
            if (requestAddressRes == null || HttpStatus.HTTP_OK != requestAddressRes.getCode()) {
                ipAddrRequestErr = true;
            }
            String requestAddressStr = "";
            if (!ipAddrRequestErr) {
                String data = requestAddressRes.getData();
                try {
                    BaiDuIpRes ipRes = JSONObject.parseObject(data, BaiDuIpRes.class);
                    if (!ipRes.getStatus().equals(BaiDuIpRes.OK_STATUS)) {
                        log.info("百度ip定位结果未返回成功:{}", ipRes);
                        ipAddrRequestErr = true;
                    } else {
                        getRequestAddressStr(requestAddressStr, ipRes);
                    }
                } catch (Exception e) {
                    log.warn("解析百度ip结果失败,{}", data);
                    ipAddrRequestErr = true;
                }
            }
            String content = emailContent;
            String uuid = IdUtil.fastSimpleUUID();
            if (ipAddrRequestErr) {
                //使用无地址内容
                content = emailContentNoAddr;
                content = StrUtil.format(content, uuid, uuid);
            } else {
                content = StrUtil.format(content, requestAddressStr, uuid, uuid);
            }

            log.info("修改密码——发送邮件，内容：{},to:{},uuid:{}", content, email, uuid);
            redisTemplate.opsForValue().set(String.format(RedisKeys.UPDATE_PWD_UID, userId), uuid, validTimeOut, TimeUnit.MINUTES);
            if (type.equals(EmailTypeEnum.UPDATE_PWD.getType())) {
                emailSender.sendHtml(subject, email, content);
            }

        });
    }

    /**
     * 修改密码
     *
     * @param rawPwd 旧密码
     * @param newPwd 新密码
     * @param userId 用户id
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public Result updatePwd(String rawPwd, String newPwd, Long userId) {
        String oldPwd = userMapper.selectPwdById(userId);
        if (bCryptPasswordEncoder.matches(rawPwd, oldPwd)) {
            //旧密码正确
            userMapper.updatePwd(bCryptPasswordEncoder.encode(newPwd), userId);
            return Result.ok("修改成功");
        } else {
            return Result.fail("旧密码错误");
        }
    }

    private void getRequestAddressStr(String requestAddressStr, BaiDuIpRes ipRes) {
        BaiDuIpRes.AddressDetail addressDetail = ipRes.getContent().getAddressDetail();
        requestAddressStr += addressDetail.getProvince() + Constants.MIDDLE_LINE_STR + addressDetail.getCity();
    }

    /**
     * 查询用户可用的认证方式
     *
     * @param userId 用户id
     * @return VerifyMethod
     */
    public Result<VerifyMethod> queryVerifyMethod(Long userId) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            return new Result<>(403, null, false, "暂无可用的认证方式");
        }
        VerifyMethod verifyMethod = new VerifyMethod();
        if (StrUtil.isNotEmpty(user.getMobile())) {
            verifyMethod.setMobile(SecurityUtil.fuzzMobile(user.getMobile()));
            verifyMethod.setMobileValid(true);
        }
        if (StrUtil.isNotEmpty(user.getEmail())) {
            verifyMethod.setEmail(SecurityUtil.fuzzEmail(user.getEmail()));
            verifyMethod.setEmailValid(true);
        }
        return new Result<>(200, verifyMethod, true, "ok");
    }

    /**
     * 百度ip地址开发API
     */
    @Value("${maimao.baidu.ipAddr}")
    private String baiduIpAddr;

    /**
     * 没有请求地址的邮件内容
     */
    @Value("${maimao.mail.updatePwd.contentNoAddr}")
    private String emailContentNoAddr;

    /**
     * 有请求地址的邮件内容
     */
    @Value("${maimao.mail.updatePwd.content}")
    private String emailContent;

    /**
     * 修改密码邮件主题
     */
    @Value("${maimao.mail.updatePwd.subject}")
    private String subject;


    @Value("${maimao.mail.updatePwd.validTimeOut}")
    private Integer validTimeOut;


    /**
     * 绑定邮箱
     *
     * @param email  邮箱
     * @param userId 用户id
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void bindEmail(String email, Long userId) {
        userMapper.updateEmailById(email, userId);
    }

    /**
     * 后台查询用户列表
     */
    public PageResult<User> listUser(User params, Integer page, Integer size) {
        PageHelper.startPage(page, size);
        List<User> users = userMapper.queryByParams(params.getId(),
                params.getUsername(),
                params.getGender(),
                params.getMobile(),
                params.getStatus());
        return new PageResult<>(users);
    }

    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void freezeRestore(Long userId, Integer status) {
        userMapper.updateStatusById(userId, status);
    }

    /**
     * 用户基本信息
     */
    public User baseInfo(Long userId) {
        User user = userMapper.baseInfo(userId);
        if (null == user) {
            return null;
        }
        user.setEmail(SecurityUtil.fuzzEmail(user.getEmail()));
        user.setMobile(SecurityUtil.fuzzMobile(user.getMobile()));
        return user;
    }

    /**
     * 修改个人信息
     *
     * @param id       id
     * @param gender   性别
     * @param birthday 生日
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void updateBaseInfo(Long id, Integer gender, LocalDate birthday) {
        userMapper.updateById(User.builder().id(id).birthday(birthday).gender(gender).build());
    }
}
